
package com.swak.frame.lock;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;

import com.swak.frame.cache.CacheProxy;
import com.swak.frame.cache.OutputType;
import com.swak.frame.exception.SwakScriptNotFoundException;
import com.swak.frame.logger.SwakLoggerFactory;

/**
 * 分布式锁
 * 
 * @ClassName: RedisDistributedLock.java
 * @author: colley.ma
 * @date: 2022/02/11
 */
public class RedisDistributedLock implements DistributedLock {
    protected Logger logger = SwakLoggerFactory.getLogger(getClass()); // 日志记录

    private static final Long RELEASE_SUCCESS = 1L;
    private static String SCRIPT =
        "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private CacheProxy cacheProxy;
    private String luaString;

    public RedisDistributedLock() {}

    public RedisDistributedLock(CacheProxy cacheProxy) {
        this.cacheProxy = cacheProxy;
    }


    @Override
    public boolean tryLock(String lockKey, String requestId, long expireTime,
        TimeUnit timeOutUnit) {
        try {
            return cacheProxy.set(lockKey, requestId, expireTime, timeOutUnit, false);
        } catch (Exception e) {
            logger.error("tryLock error", e);
            try {
                return cacheProxy.set(lockKey, requestId, expireTime, timeOutUnit, false);
            } catch (Exception ex) {
                logger.error("tryLock error", ex);
            }
        }
        // 异常获取锁失败
        return false;
    }


    @Override
    public boolean tryLock(String lockKey, String requestId) {
        return this.tryLock(lockKey, requestId, 300, TimeUnit.SECONDS);
    }

    /**
     * 释放分布式锁
     */
    @Override
    public boolean unLock(String lockKey, String requestId) {
        Object result = 0;
        try {
            try {
                result = cacheProxy.evalsha(luaString, Collections.singletonList(lockKey),
                    Collections.singletonList(requestId), false, OutputType.INTEGER);
            } catch (SwakScriptNotFoundException e) {
                this.luaString = cacheProxy.scriptLoad(SCRIPT);
                result = cacheProxy.evalsha(luaString, Collections.singletonList(lockKey),
                    Collections.singletonList(requestId), false, OutputType.INTEGER);
            }
        } catch (Exception e) {
            logger.error("unLock error,lockKey:" + lockKey, e);
        }

        return RELEASE_SUCCESS.equals(result);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.luaString = cacheProxy.scriptLoad(SCRIPT);

    }

    public void setCacheProxy(CacheProxy cacheProxy) {
        this.cacheProxy = cacheProxy;
    }

    @Override
    public boolean tryLock(String lockKey, String requestId, long seconds) {
        return this.tryLock(lockKey, requestId, seconds, TimeUnit.SECONDS);
    }
}
